Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nc account by id #8167

Closed
wants to merge 1 commit into from
Closed

Conversation

alphaprinz
Copy link
Contributor

@alphaprinz alphaprinz commented Jun 26, 2024

Explain the changes

  1. This PR removes references of account name from bucket json files.
  2. The bucket_owner field is removed from bucket schema.
  3. Principal in bucket policy is account id instead of account name.
  4. In order to find account id by account name, a new dir root_accounts was added with symlinks from account name to account json.

Issues: Fixed #xxx / Gap #xxx

  1. A Change to account name doesn't require updating bucket json files. bugs:
    Account update with account name is not updating bucket_name.json file automatically #8080
    NSFS | NC | Bucket owner & Principal of bucket policies should be ids and not email/name #7734 (though we still allow root account name as principal for backward compatibility).

Testing Instructions:

  1. Same tests
  • Doc added/updated
  • Tests added

NooBaa Non Containerized Components Diagram  (added root accounts 230724)

@alphaprinz alphaprinz changed the title Nc account by Nc account by id Jun 26, 2024
@alphaprinz alphaprinz force-pushed the nc_account_by_id branch 3 times, most recently from 2a5df3c to d860c93 Compare June 27, 2024 05:28
src/manage_nsfs/manage_nsfs_cli_utils.js Outdated Show resolved Hide resolved
src/manage_nsfs/manage_nsfs_cli_utils.js Outdated Show resolved Hide resolved
src/manage_nsfs/manage_nsfs_validations.js Outdated Show resolved Hide resolved
src/manage_nsfs/manage_nsfs_validations.js Outdated Show resolved Hide resolved
src/sdk/bucketspace_fs.js Show resolved Hide resolved
Comment on lines 156 to 164
const account_config_path = this._get_account_config_path(bucket.owner_account);
data = (await nb_native().fs.readFile(this.fs_context, account_config_path)).data;
const account = JSON.parse(data.toString());
nsfs_schema_utils.validate_account_schema(account);
is_valid = await this.check_bucket_config(bucket);
if (!is_valid) {
dbg.warn('BucketSpaceFS: account linked to bucket is not valid: ', name);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this addition?
Please also add in comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we had the account name in the bucket json, now we don't.
In order to get the account name, I need to go through the same code as reading the bucket json.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why to add an account schema check when we get its name.
And it seems that you check the bucket config twice:
(line 152) let is_valid = await this.check_bucket_config(bucket);
(line 160) is_valid = await this.check_bucket_config(bucket);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same reasoning as checking the bucket schema when getting bucket data. I just went with the pattern already in the code.
Regarding the double is_valid, it's mistake, thanks for noticing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure you need to do it...
Imagine that something went wrong with the account (somehow it contains a property not in the schema), and you are trying to read it bucket and you will get an error on validation of the account (although it is not related to reading the bucket).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a problem in any json schema is a valid reason for a request to fail, specifically if that json contains information the request is supposed to return.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not fail a request in bucketspace if the account config that created it somehow is not valid.

  • Let's say we have:
    • an account (root account): account1
    • the account owns a bucket: bucket1
      • it has a bucket policy for all S3 actions for account1 and account2 (additional root account).
  • account2 sends requests on bucket bucket1 (he has the permission, so all the requests succeeded).
  • somehow account1 schema became invalid

what should happen to account2' requests?
In my opinion, his requests should still be succeeded...

src/sdk/bucketspace_fs.js Show resolved Hide resolved
src/cmd/manage_nsfs.js Outdated Show resolved Hide resolved
@alphaprinz alphaprinz force-pushed the nc_account_by_id branch 6 times, most recently from 2f227ae to da61744 Compare June 27, 2024 22:21
@alphaprinz alphaprinz force-pushed the nc_account_by_id branch 9 times, most recently from 2284ee4 to 2ca3cd6 Compare July 1, 2024 17:51
src/endpoint/s3/s3_rest.js Outdated Show resolved Hide resolved
src/test/unit_tests/test_s3_bucket_policy.js Outdated Show resolved Hide resolved
src/cmd/manage_nsfs.js Outdated Show resolved Hide resolved
if (action === ACTIONS.ADD || action === ACTIONS.UPDATE) {
if (action === ACTIONS.ADD) native_fs_utils.validate_bucket_creation({ name: data.name });
if (action === ACTIONS.UPDATE && !_.isUndefined(data.new_name)) native_fs_utils.validate_bucket_creation({ name: data.new_name });

if (action === ACTIONS.ADD && _.isUndefined(data.bucket_owner)) throw_cli_error(ManageCLIError.MissingBucketOwnerFlag);
if (action === ACTIONS.ADD && _.isUndefined(data.owner_account)) throw_cli_error(ManageCLIError.MissingBucketOwnerFlag);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the new code changes it seems to fail in get_bucket_owner_account with an undefined name and not with the error MissingBucketOwnerFlag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'm with you. Where do you see this failure? In what scenario? A test?
Are you talking about get_bucket_owner_account in fetch_bucket_data()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I meant for a manual test that you can run with creating a bucket without passing the ---owner flag, which error do you see? (In this line it should be MissingBucketOwnerFlag, but I saw something else as mentioned).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Since fetch_bucket_data() is before validate_bucket_args(), I've added a check for it in fetch_bucket_data().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are trying to validate as early as we can, maybe you can do it before the fetch_bucket_data in a function under validate_input_types?
I think it might be a bit confusing to see validations in the fetch_bucket_data.

@alphaprinz alphaprinz force-pushed the nc_account_by_id branch 2 times, most recently from 3e16ef7 to 5cb4178 Compare July 3, 2024 18:34
docs/NooBaaNonContainerized/Configuration.md Outdated Show resolved Hide resolved
docs/NooBaaNonContainerized/GettingStarted.md Outdated Show resolved Hide resolved
docs/NooBaaNonContainerized/NooBaaCLI.md Show resolved Hide resolved
docs/NooBaaNonContainerized/NooBaaCLI.md Outdated Show resolved Hide resolved
docs/NooBaaNonContainerized/NooBaaCLI.md Outdated Show resolved Hide resolved
@@ -351,7 +362,7 @@ class AccountSpaceFS {
try {
const requesting_account = account_sdk.requesting_account;
const access_key_id = params.access_key;
const requester = this._check_if_requesting_account_is_root_account_or_user_om_himself(action,
this._check_if_requesting_account_is_root_account_or_user_om_himself(action,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you ignore the return value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going by access key, I don't need to deal with names.
Unless I'm missing sth?

@@ -402,7 +406,7 @@ class AccountSpaceFS {
try {
const requesting_account = account_sdk.requesting_account;
const access_key_id = params.access_key;
const requester = this._check_if_requesting_account_is_root_account_or_user_om_himself(action,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the option for user-name as optional, let's see this change after merging PR #8233.

src/sdk/accountspace_fs.js Show resolved Hide resolved
src/sdk/accountspace_fs.js Outdated Show resolved Hide resolved
Comment on lines 232 to 247
const account_identifier_name = req.object_sdk.nsfs_config_root ? account.name.unwrap() : account.email.unwrap();
const account_identifier_id = req.object_sdk.nsfs_config_root ? account._id : undefined;
//In old buckets (until 5.17), system_owner is account name (sensitive string).
//In new buckets (since 5.18), system_owner is account id (string).
const system_owner_equatable = system_owner.unwrap ? system_owner.unwrap() : system_owner;
const is_system_owner =
(account_identifier_name === system_owner_equatable) ||
(account_identifier_id && account_identifier_id === system_owner_equatable);

// @TODO: System owner as a construct should be removed - Temporary
if (is_system_owner) return;

const is_owner = (function() {
if (account.bucket_claim_owner && account.bucket_claim_owner.unwrap() === req.params.bucket) return true;
if (owner_account && owner_account.id === account._id) return true;
if (account_identifier === bucket_owner.unwrap()) return true;
if (req.object_sdk.nsfs_config_root && account._id === owner_account.id) return true; // NC NSFS case
if (!req.object_sdk.nsfs_config_root && account_identifier_name === bucket_owner.unwrap()) return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that s3 rest api should look at nsfs_config_root at all, this is why we have an AccountSpace and BucketSpace interfaces. It seems that the use of this variable somehow has spread around in the code (even into ssl_utils god forbid). Instead, the code that does not directly writes/reads from the config dir should just look at the actual bucket and account config structures it received from read_bucket_sdk_policy_info and requesting_account, and use their information to make the ownership checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I agree, but practically I went with the existing-
const account_identifier = req.object_sdk.nsfs_config_root ? account.name.unwrap() : account.email.unwrap();

I can try to remove what I have added.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at it, and I don't see a quick/simple solution to differentiate between by-name or by-id ownership (in s3_rest).
If there's something I'm missing please let me know.
I can go by whether account_identifier_id is defined to reduce the foot print of req.object_sdk.nsfs_config_root in the code (but it's equivalent).

docs/NooBaaNonContainerized/NooBaaCLI.md Outdated Show resolved Hide resolved
docs/NooBaaNonContainerized/NooBaaCLI.md Outdated Show resolved Hide resolved
docs/NooBaaNonContainerized/NooBaaCLI.md Outdated Show resolved Hide resolved
docs/NooBaaNonContainerized/NooBaaCLI.md Show resolved Hide resolved
src/manage_nsfs/manage_nsfs_help_utils.js Show resolved Hide resolved
src/manage_nsfs/manage_nsfs_cli_utils.js Outdated Show resolved Hide resolved
@@ -168,7 +191,7 @@ async function get_options_from_file(file_path) {
* @param {object[]} access_keys
*/
function has_access_keys(access_keys) {
return access_keys.length > 0;
return access_keys.length > 0 && access_keys[0].access_key;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want it, because the scenario of the access_keys object to be {access_key: undefined, secret_key: undefined} should not be a real data in a config, it is "by-product" of one of the functions.
In PR #8238 I'm suggesting to change it without changing this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way is fine by me.

src/sdk/bucketspace_fs.js Outdated Show resolved Hide resolved
src/sdk/accountspace_fs.js Outdated Show resolved Hide resolved
src/sdk/accountspace_fs.js Show resolved Hide resolved
@alphaprinz alphaprinz force-pushed the nc_account_by_id branch 4 times, most recently from 5a7b7ae to 4be39ac Compare July 30, 2024 23:41
const account_identifier_id = req.object_sdk.nsfs_config_root ? account._id : undefined;
//In old buckets (until 5.17), system_owner is account name (sensitive string).
//In new buckets (since 5.18), system_owner is account id (string).
const system_owner_equatable = system_owner.unwrap ? system_owner.unwrap() : system_owner;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure about this line? system_owner.unwrap? (did you mean the function unwrap()?)
Are you trying to check if it is an ID or a sensitive string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Sometimes it is a sensitive string, but in nc it's an id.
I think system owner is going to be removed anyway, no?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have PR #8192 to remove the system owner (was not reviewed yet).

But still system_owner.unwrap will be always undefined (because it searches for the property unwrap in the object system_owner), better write the condition in the way you want, for example:
some_var instanceof SensitiveString ? some_var.unwrap() : some_var;

Comment on lines -161 to -167
} else {
const requested_account_encrypted = await nc_mkm.encrypt_access_keys(requested_account);
const account_string = JSON.stringify(requested_account_encrypted);
nsfs_schema_utils.validate_account_schema(JSON.parse(account_string));
await native_fs_utils.update_config_file(this.fs_context, this.accounts_dir,
account_config_path, account_string);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I want to update the json whether name was changed or not.

async _update_account_config_new_username(action, user_id, old_username, new_username, requesting_account) {
const root_account_name = await this._get_root_account_name(requesting_account);
await this._check_username_already_exists(action, new_username, root_account_name);
const root_account_old_name = get_symlink_config_file_path(this.root_accounts_dir, old_username, root_account_name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm missing something, why there is a change in the root account name in the general code of updating the username?
Could you please add comments of the steps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For iam account name change, the root account name does not change.
If you change the name of an iam account, you need to update its by-name symlink.
Part of the by-name symlink is the root account name.
Maybe the params names are confusing.

const account_identifier_id = req.object_sdk.nsfs_config_root ? account._id : undefined;
//In old buckets (until 5.17), system_owner is account name (sensitive string).
//In new buckets (since 5.18), system_owner is account id (string).
const system_owner_equatable = system_owner.unwrap ? system_owner.unwrap() : system_owner;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have PR #8192 to remove the system owner (was not reviewed yet).

But still system_owner.unwrap will be always undefined (because it searches for the property unwrap in the object system_owner), better write the condition in the way you want, for example:
some_var instanceof SensitiveString ? some_var.unwrap() : some_var;

src/sdk/accountspace_fs.js Outdated Show resolved Hide resolved
@alphaprinz alphaprinz force-pushed the nc_account_by_id branch 6 times, most recently from 5568f50 to 834d49a Compare July 31, 2024 20:42
previously known as "account by id"

Main change is that accounts/ dir now has <account_id>.json instead of <account_name>.json file.
By-name indexing is in new root_accounts/<root_account_name>/<iam_account_name>.symlink.

Signed-off-by: Amit Prinz Setter <[email protected]>
This was referenced Aug 12, 2024
shirady added a commit to shirady/noobaa-core that referenced this pull request Aug 28, 2024
This PR is based on @alphaprinz noobaa#8167 PR.

1. In s3_rest under the function authorize_request_policy  rename account_identifier to account_identifier_name and add account_identifier_id.
2. In bucketspace_fs where we use put_bucket_policy and in manage_nsfs_validations where we validate the bucket policy use is_account_exists_by_principal that accepts both account name and id.
3. Both in authorize_request_policy (s3_rest) and has_bucket_action_permission (bucketspace_fs) check the principal by ID first and then the principal by name.

Signed-off-by: shirady <[email protected]>
@nimrod-becker
Copy link
Contributor

superseded by
#8326 #8289 #8279 #8280 #8312

dannyzaken pushed a commit to dannyzaken/noobaa-core that referenced this pull request Sep 29, 2024
This PR is based on @alphaprinz noobaa#8167 PR.

1. In s3_rest under the function authorize_request_policy  rename account_identifier to account_identifier_name and add account_identifier_id.
2. In bucketspace_fs where we use put_bucket_policy and in manage_nsfs_validations where we validate the bucket policy use is_account_exists_by_principal that accepts both account name and id.
3. Both in authorize_request_policy (s3_rest) and has_bucket_action_permission (bucketspace_fs) check the principal by ID first and then the principal by name.

Signed-off-by: shirady <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants